package org.jeecg.modules.ws.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.codehaus.groovy.runtime.wrappers.Wrapper;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.ws.config.FileConfig;
import org.jeecg.modules.ws.dto.ExportSheetDataDto;
import org.jeecg.modules.ws.dto.UploadSheetDataDto;
import org.jeecg.modules.ws.dto.VerifySheetDataDto;
import org.jeecg.modules.ws.entity.WsBondInfo;
import org.jeecg.modules.ws.entity.WsEmployee;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

import static com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery;

/**
 * @author ZJJ
 * @date 2025-04-23 下午4:06
 * @copyright Copyright (c) 2025 ZJJ
 */
@Slf4j
@Component
public class ExcelUtil<T> {

    @Value("${jeecg.path.upload}")
    private String upLoadPath;

    public List<T> excelVerify(HttpServletRequest request, HttpServletResponse response,List<VerifySheetDataDto<T>> sheetDataList) {
        List<T> list = new ArrayList<>();
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile file = entity.getValue();
            // 文件校验
            try {
                InputStream inputStream = file.getInputStream();
                int sheetIndex =  0;
                for (VerifySheetDataDto<T> sheetDataDto : sheetDataList) {
                    List<String> params = sheetDataDto.getParams();
                    Class<T> clazz = sheetDataDto.getClazz();
                    List<T> excelSheet = readExcelSheet(inputStream, clazz, 1, sheetIndex, params);
                    List<T> ts = verifyRepeatList(excelSheet, sheetDataDto.getService(), params);
                    if (!ts.isEmpty()) {
                        list.addAll(ts);
                    }
                    sheetIndex++;
                }
            } catch (Exception e) {
                log.error("校验excel文件数据异常:{}",e.getMessage(), e);
                throw new JeecgBootException(e.getMessage());
            }
        }
        return list;
    }

    private List<T> verifyRepeatList(List<T> excelSheet, IService<T> service, List<String> params) {
        List<T> list = new ArrayList<>();
        for (T obj : excelSheet) {
            QueryWrapper<T> queryWrapper = buildQueryWrapper(obj, params);
            List<T> ts = service.list(queryWrapper);
            if (ts != null && !ts.isEmpty()) {
                list.addAll(ts);
            }
        }
        return list;
    }

    /**
     * 自定义导出Sheet
     *
     * @param data      导出的数据列表
     * @param title     导出文件的标题
     * @param titleMap  自定义表头信息，键为表头，值为字段名
     * @return ModelAndView
     */
    public Map<String, Object>  customPackageSheet(List<Map<String, Object>> data, String title, Map<String, String> titleMap) {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        // Step.4 设置导出参数
        ExportParams exportParams = new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title + "报表");
        exportParams.setImageBasePath(upLoadPath);

        // 设置自定义表头
        List<ExcelExportEntity> entityList = new ArrayList<>();
        for (Map.Entry<String, String> entry : titleMap.entrySet()) {
            ExcelExportEntity entity = new ExcelExportEntity(entry.getKey(), entry.getValue());
            entityList.add(entity);
        }

        // 设置数据和表头
        Map<String, Object> map = new HashMap<>();
        map.put(NormalExcelConstants.PARAMS, exportParams);
        map.put(NormalExcelConstants.EXPORT_FIELDS, entityList);
        map.put(NormalExcelConstants.DATA_LIST, data);
        return map;
    }

    /**
     * 导出excel
     * @param title
     * @param mapList
     * @return
     */
    public ModelAndView exportXls(String title,List<Map<String,Object>> mapList){
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, title);
        mv.addObject(NormalExcelConstants.MAP_LIST, mapList);
        return mv;
    }

    /**
     * 封装sheet
     *
     */
    public List<Map<String, Object>>  packageSheet(List<ExportSheetDataDto<?>> sheetDataList) {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<Map<String, Object>> listMap = new ArrayList<>();
        int sheetIndex = 1;
        for (ExportSheetDataDto<?> sheetDataDto : sheetDataList) {
            List exportList = sheetDataDto.getList();
            Class clazz = sheetDataDto.getClazz();
            String title = sheetDataDto.getTitle();
            Map<String, Object> map = new HashMap<>();
            ExportParams exportParams = new ExportParams(title + "报表 - Sheet" + sheetIndex, "导出人:" + sysUser.getRealname(), title + " - Sheet" + sheetIndex);
            exportParams.setImageBasePath(upLoadPath);
            map.put(NormalExcelConstants.PARAMS, exportParams);
            map.put(NormalExcelConstants.CLASS, clazz);
            map.put(NormalExcelConstants.DATA_LIST, exportList);
            listMap.add(map);

            sheetIndex++;
        }
        return listMap;
    }

    @Transactional(rollbackFor = Exception.class)
    public Result<?> importExcel(InputStream inputStream, List<UploadSheetDataDto<T>> sheetDataList) {
        try {
            int sheetIndex = 0;
            for (UploadSheetDataDto<T> sheetData : sheetDataList) {
                Class<T> clazz = sheetData.getClazz();
                IService<T> service = sheetData.getService();
                List<String> params = sheetData.getParams();

                List<T> list = readExcelSheet(inputStream, clazz, 1, sheetIndex, params);
                saveData(list, service, params);

                sheetIndex++;
            }
        }catch (Exception e){
            log.error("导入文件异常:{}",e.getMessage(), e);
            throw new JeecgBootException(e.getMessage());
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return Result.OK("文件导入成功！");
    }

    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response, List<UploadSheetDataDto<T>> sheetDataList) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            InputStream inputStream =null;
            MultipartFile file = entity.getValue(); // 获取上传文件对象
            try {
                int sheetIndex = 0;
                for (UploadSheetDataDto<T> sheetData : sheetDataList) {
                    if (inputStream!=null){
                        inputStream.close();
                    }
                    inputStream = file.getInputStream();
                    Class<T> clazz = sheetData.getClazz();
                    IService<T> service = sheetData.getService();
                    List<String> params = sheetData.getParams();

                    List<T> list = readExcelSheet(inputStream, clazz, 1, sheetIndex, params);
                    saveData(list, service, params);

                    sheetIndex++;
                }

                return Result.ok("文件导入成功！");
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                log.error("文件导入失败：" + e.getMessage());
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    if (inputStream != null){
                        inputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.error("文件导入失败！");
    }

    private <E> List<E> readExcelSheet(InputStream inputStream, Class<E> clazz, int sheetNum, int startSheetIndex, List<String> params) throws Exception {
        ImportParams importParams = new ImportParams();
        importParams.setTitleRows(2);
        importParams.setHeadRows(1);
        importParams.setSheetNum(sheetNum);
        importParams.setStartSheetIndex(startSheetIndex);
        return ExcelImportUtil.importExcel(inputStream, clazz, importParams);
    }

    private <E> void saveData(List<E> list, IService<E> service, List<String> params) {
        for (E obj : list) {
            if (params.isEmpty()){
                service.save(obj);
            }else {
                QueryWrapper<E> queryWrapper = buildQueryWrapper(obj, params);
                service.saveOrUpdate(obj, queryWrapper);
            }
        }
    }

    private <E> QueryWrapper<E> buildQueryWrapper(E obj, List<String> params) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        Class<?> clazz = obj.getClass();
        for (String param : params) {
            try {
                Field field = clazz.getDeclaredField(toCamelCase(param));
                field.setAccessible(true);
                queryWrapper.eq(param, field.get(obj));
            } catch (Exception e) {
                throw new JeecgBootException("组装Wrapper更新条件时异常:" + e.getMessage());
            }
        }
        return queryWrapper;
    }

    /**
     * 将下划线命名法转换为驼峰命名法
     *
     * @param underscoreStr 下划线命名法的字符串
     * @return 驼峰命名法的字符串
     */
    public static String toCamelCase(String underscoreStr) {
        if (underscoreStr == null || underscoreStr.isEmpty()) {
            return underscoreStr;
        }

        StringBuilder camelCaseStr = new StringBuilder();
        boolean nextUpperCase = false;

        for (char c : underscoreStr.toCharArray()) {
            if (c == '_') {
                nextUpperCase = true;
            } else {
                if (nextUpperCase) {
                    camelCaseStr.append(Character.toUpperCase(c));
                    nextUpperCase = false;
                } else {
                    camelCaseStr.append(Character.toLowerCase(c));
                }
            }
        }

        return camelCaseStr.toString();
    }

    @Autowired
    private FileConfig fileConfig;

    /**
     * 上传临时文件
     * @param request
     */
    public List<String>  uploadTemporaryFile(HttpServletRequest request){
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        List<String> redisList = new ArrayList<>();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile mf = entity.getValue();
            StringBuilder bizPath = new StringBuilder();
            String temporaryFilePath = fileConfig.getTemporaryFilePath();
            String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
            bizPath.append(temporaryFilePath).append(File.separator);
            bizPath.append(uuid).append(File.separator);
            String filePath = bizPath.toString();
            File file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();// 创建文件根目录
            }
            String fileName = mf.getOriginalFilename();
            String savePath = file.getPath() + File.separator + fileName;
            File savefile = new File(savePath);
            try {
                FileCopyUtils.copy(mf.getBytes(), savefile);
            } catch (IOException e) {
                log.error("上传临时文件异常:{}",e.getMessage(), e);
                throw new RuntimeException(e);
            }
            redisList.add(savePath);
        }
        return redisList;
    }
}
